﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using Framework.Extensions;
namespace Framework.Data
{
    public class View<T> : DynamicQuery, IView<T> where T : new()
    {
        protected string DatabaseObjectName { get; set; }
        protected IEnumerable<string> Keys { get; private set; }

        public View(IDatabase database, string viewName, string primaryKeyField)
            : base(database)
        {
            DatabaseObjectName = !string.IsNullOrWhiteSpace(viewName) ? viewName : typeof(T).ClassName();
            Keys = !string.IsNullOrWhiteSpace(primaryKeyField) ? primaryKeyField.Split(',').Select(x => x.Trim()) : RuntimeDetectionOfKeyFields();
        }

        public View(IDatabase database, string viewName)
            : this(database, viewName, null)
        {
        }

        public View(IDatabase database)
            : this(database, null)
        {
        }

        private IEnumerable<string> RuntimeDetectionOfKeyFields()
        {
            var res = typeof(T)
                    .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                    .Where(x =>
                        x.GetCustomAttributes(typeof(KeyAttribute), true)
                        .Cast<KeyAttribute>()
                        .Select(y => y != null).FirstOrDefault())
                    .Select(x => x.Name)
                    .ToList();
            if (res.Count == 0)
            {
                typeof(T)
                    .GetProperties(BindingFlags.Instance | BindingFlags.Public)
                    .Where(x => x.Name == "Id" || x.Name == DatabaseObjectName + "Id")
                    .Select(x => x.Name)
                    .ToList();
            }
            return res;
        }

        public IEnumerable<T> All()
        {
            return Where("", null); ;
        }

        public IEnumerable<T> All(string orderBy)
        {
            string sql = ImplementationDetails.GetSelectStatement(DatabaseObjectName, "*", "", orderBy);
            return base.Query<T>(sql, null);
        }

        public IEnumerable<T> All(int startPage, int pageSize)
        {
            return Paged("", "", startPage, pageSize, null).Data;
        }

        public IEnumerable<T> All(string orderBy, int startPage, int pageSize)
        {
            return Paged("", orderBy, startPage, pageSize, null).Data;
        }

        public IEnumerable<T> Where(string where = "", params object[] args)
        {
            string sql = ImplementationDetails.GetSelectStatement(DatabaseObjectName, "*", where, "");
            return base.Query<T>(sql, args);
        }

        public IPagedResult<T> Paged(string where = "", string orderBy = "", int pageNumber = 1, int pageSize = 20, params object[] args)
        {
            var keyList = Keys.ToString(",");
            var recordStart = (pageNumber - 1) * pageSize;

            if (String.IsNullOrEmpty(orderBy)) orderBy = keyList;

            var countSQL = ImplementationDetails.GetCountStatement(DatabaseObjectName, where);
            var total = Scalar(countSQL, args);
            int TotalRecords = 0;

            if (total is int) TotalRecords = (int)total;
            else if (total is long) TotalRecords = Convert.ToInt32(total);

            IEnumerable<T> results;
            if (ImplementationDetails.SupportsPagedStatements)
            {
                var sql = ImplementationDetails.GetPagedStatement(DatabaseObjectName, orderBy, recordStart, pageSize, "*", where);
                results = base.Query<T>(sql, args);
            }
            else
            {
                var sql = ImplementationDetails.GetSelectStatement(DatabaseObjectName, "*", where, orderBy);
                results = base.Query<T>(sql, args).Skip(recordStart).Take(pageSize);
            }
            return new PagedResult<T>(results, pageNumber, TotalRecords);
        }

        public IEnumerable<T> SQL(string sql, params object[] args)
        {
            return base.Query<T>(sql, args);
        }
    }
}
